原題:
https://cses.fi/problemset/task/1675
題意:
有 n 城市與m 條雙向道路候選,每條路有修復成本。要把所有城市連通且總成本最小;若無法連通,輸出 IMPOSSIBLE。
解題思路
#include <bits/stdc++.h>
using namespace std;
struct DSU {
    vector<int> p, sz;
    DSU(int n=0){ init(n); }
    void init(int n){
        p.resize(n+1); sz.assign(n+1,1);
        iota(p.begin(), p.end(), 0);
    }
    int find(int x){ return p[x]==x?x:p[x]=find(p[x]); }
    bool unite(int a,int b){
        a=find(a); b=find(b);
        if(a==b) return false;
        if(sz[a]<sz[b]) swap(a,b);
        p[b]=a; sz[a]+=sz[b];
        return true;
    }
};
int main(){
    int n, m;
    cin>>n>>m;
    struct E{int u,v; long long w;};
    vector<E> edges(m);
    for(auto &e: edges) cin>>e.u>>e.v>>e.w;
    sort(edges.begin(), edges.end(), [](const E&a,const E&b){return a.w<b.w;});
    DSU dsu(n);
    long long cost=0; int used=0;
    for(auto &e: edges){
        if(dsu.unite(e.u,e.v)){
            cost+=e.w; used++;
            if(used==n-1) break;
        }
    }
    if(used==n-1) cout<<cost<<"\n";
    else cout<<"IMPOSSIBLE\n";
    return 0;
}
原題:
https://cses.fi/problemset/task/1676
題意:
給 n 個城市,逐一加入 m 條無向邊。每加入一條邊後,輸出:
解題思路:
用 DSU 維護集合大小與分量數。
初始化分量數 = n,最大分量 = 1。每次 unite(u,v) 成功就分量數 -1、更新最大分量。
複雜度:O((n+m)α(n))。
#include <bits/stdc++.h>
using namespace std;
struct DSU{
    vector<int> p, sz;
    DSU(int n=0){ init(n); }
    void init(int n){
        p.resize(n+1); sz.assign(n+1,1);
        iota(p.begin(), p.end(), 0);
    }
    int find(int x){ return p[x]==x?x:p[x]=find(p[x]); }
    bool unite(int a,int b, int &mx){
        a=find(a); b=find(b);
        if(a==b) return false;
        if(sz[a]<sz[b]) swap(a,b);
        p[b]=a; sz[a]+=sz[b];
        mx = max(mx, sz[a]);
        return true;
    }
    int size(int x){ return sz[find(x)]; }
};
int main(){
    int n, m;
    cin>>n>>m;
    DSU dsu(n);
    int comps = n, mx = 1;
    while(m--){
        int u,v; cin>>u>>v;
        if(dsu.unite(u,v,mx)) comps--;
        cout << comps << " " << mx << "\n";
    }
    return 0;
}
原題:
https://leetcode.com/problems/min-cost-to-connect-all-points/description/
題意:
有 n 個點,連線成本為曼哈頓距離,求連通全部點的最小成本。
解題思路:
這是完全圖的 MST。可用 Prim。
示範 Kruskal:
生成所有邊(至多 n(n−1)/2 條,n=1000 時約 50 萬),排序後用 DSU 選邊。
class Solution {
public:
    struct DSU{
        vector<int> p, sz;
        DSU(int n=0){ init(n); }
        void init(int n){ p.resize(n); sz.assign(n,1); iota(p.begin(),p.end(),0); }
        int find(int x){ return p[x]==x?x:p[x]=find(p[x]); }
        bool unite(int a,int b){
            a=find(a); b=find(b);
            if(a==b) return false;
            if(sz[a]<sz[b]) swap(a,b);
            p[b]=a; sz[a]+=sz[b];
            return true;
        }
    };
    int minCostConnectPoints(vector<vector<int>>& points) {
        int n = points.size();
        struct E{int w,u,v;};
        vector<E> edges; edges.reserve(n*(n-1)/2);
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                int w = abs(points[i][0]-points[j][0]) + abs(points[i][1]-points[j][1]);
                edges.push_back({w,i,j});
            }
        }
        sort(edges.begin(), edges.end(), [](const E&a,const E&b){return a.w<b.w;});
        DSU dsu(n);
        long long ans=0; int used=0;
        for(auto &e: edges){
            if(dsu.unite(e.u,e.v)){
                ans+=e.w; used++;
                if(used==n-1) break;
            }
        }
        return (int)ans;
    }
};
原題:
https://leetcode.com/problems/number-of-operations-to-make-network-connected/description/
題意:
有 n 台電腦與一些無向連線 connections[i] = [u, v]。
一次操作可以把多餘的線(即在同一連通分量內造成多餘、可移除的邊)重新接到兩個不同分量之間。
問最少需要幾次操作使整體連通;若不可能則回傳 -1。
解題思路:
class Solution {
public:
    struct DSU{
        vector<int> p, sz;
        DSU(int n=0){ init(n); }
        void init(int n){ p.resize(n); sz.assign(n,1); iota(p.begin(), p.end(), 0); }
        int find(int x){ return p[x]==x?x:p[x]=find(p[x]); }
        bool unite(int a,int b){
            a=find(a); b=find(b);
            if(a==b) return false;
            if(sz[a]<sz[b]) swap(a,b);
            p[b]=a; sz[a]+=sz[b];
            return true;
        }
    };
    int makeConnected(int n, vector<vector<int>>& connections) {
        int m = (int)connections.size();
        if(m < n-1) return -1;           // 邊不夠,必然無解
        DSU dsu(n);
        for(auto &e: connections) dsu.unite(e[0], e[1]);
        int comps = 0;
        for(int i=0;i<n;i++) if(dsu.find(i)==i) comps++;
        return comps - 1;                 // 需要把分量接起來
    }
};